/*
 * as_lesson_yuv.cpp
 *
 *  This is a file to show how to use SDL2 to render yuv in android
 *      Author: Tocy
 *		any questions please contact me by zyvj@qq.com.
 */

#include "as_lesson_log.h"
#include "SDL.h"

SDL_Window * window = NULL;
SDL_Renderer * render = NULL;
SDL_Texture * texture = NULL;

struct YuvFrame
{
    int width, height;
    int frame_size;
    unsigned char * data[3]; // for y, u, v
    unsigned char stride[3];
} yuv_frame = {0};


static bool init()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) == -1) {
        LOGE("SDL_Init failed %s", SDL_GetError());
		return -1;
    }

    LOGI("SDL_CreateWindow");
    window = SDL_CreateWindow("Hello World!", 100, 100, 640, 480,
            SDL_WINDOW_SHOWN);
    if (window == NULL) {
        LOGE("SDL_CreateWindow failed  %s", SDL_GetError());
    }
    
    LOGI("SDL_CreateRenderer");
    render = SDL_CreateRenderer(window, -1,
            SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
    if (render == NULL) {
        LOGE("SDL_CreateRenderer failed  %s", SDL_GetError());
    }
    
    return NULL != window && NULL != render;
}

static void deinit()
{
    if (NULL != render)
    {
        SDL_DestroyRenderer(render);
        render = NULL;
    }
    if (NULL != window)
    {
        SDL_DestroyWindow(window);
        window = NULL;
    }
}

static bool load_yuv(const char * path)
{
	FILE * fin = fopen(path, "rb");
	if (NULL == fin)
	{
	    LOGE("%s %d open file failed path: %s", __FUNCTION__, __LINE__, path);
		return false;
	}
	
	int w = yuv_frame.width;
    int h = yuv_frame.height;
    // alloc buffer and read yuv file
	int yuv_frame_size = w * h * 3 >> 1;
	unsigned char * yuv_data = new unsigned char[yuv_frame_size];

	fread(yuv_data, 1, yuv_frame_size, fin);

	fclose(fin);
	
	yuv_frame.frame_size = yuv_frame_size;
	yuv_frame.stride[0] = w;
	yuv_frame.stride[1] = yuv_frame.stride[2] = (w >> 1);
	int plane_size = w * h;
	yuv_frame.data[0] = yuv_data;
	yuv_frame.data[1] = yuv_data + plane_size;
	yuv_frame.data[2] = yuv_data + (plane_size >> 2);
		
	return true;	    
}
static void unload_yuv()
{
    if (NULL != yuv_frame.data[0])
    {
        delete [] yuv_frame.data[0];
        yuv_frame.data[0] = yuv_frame.data[1] = yuv_frame.data[2] = NULL;
    }
}

static bool texture_create(int w, int h)
{
    texture = SDL_CreateTexture(render, SDL_PIXELFORMAT_IYUV, 
		SDL_TEXTUREACCESS_STREAMING, w, h);
    if (NULL == texture)
    {
        LOGE("%s %d createtexture failed %s", __FUNCTION__, __LINE__, SDL_GetError());
    }
    
    return NULL != texture;
}
static void texture_update()
{
    void * pixel = NULL;
	int pitch = 0;
	if(0 == SDL_LockTexture(texture, NULL, &pixel, &pitch))
	{
		// 如果不考虑数据对齐，直接拷贝YUV数据是没有问题的
		if (pitch == yuv_frame.width)
		{
			memcpy(pixel, yuv_frame.data[0], yuv_frame.frame_size);
		}
		else // 可能发生pitch > width的情况
		{
			// 如果有数据对齐的情况，单独拷贝每一行数据
			// for Y
			int h = yuv_frame.height;
			int w = yuv_frame.width;
			unsigned char * dst = reinterpret_cast<unsigned char *>(pixel);
			unsigned char * src = yuv_frame.data[0];
			for (int i = 0; i < h; ++i)
			{
				memcpy(dst, src, w);
				dst += pitch;
				src += w;
			}

			h >>= 1;
			w >>= 1;
			pitch >>= 1;			
			// for U
			src = yuv_frame.data[1];
			for (int i = 0; i < h; ++i)
			{
				memcpy(dst, src, w);
				dst += pitch;
				src += w;
			}

			// for V
			src = yuv_frame.data[2];
			for (int i = 0; i < h; ++i)
			{
				memcpy(dst, src, w);
				dst += pitch;
				src += w;
			}
		}

		SDL_UnlockTexture(texture);
	}
}
static void texture_render()
{
    SDL_RenderClear(render);
    SDL_RenderCopy(render, texture, NULL, NULL);
    SDL_RenderPresent(render);
}
static void texture_destory()
{
    if (NULL != texture)
    {
        SDL_DestroyTexture(texture);
        texture = NULL;
    }
}

extern "C" int yuv_main(int argc, char *argv[]) 
{
    // init sdl
    if (!init())
    {
        LOGE("%s %d SDL init failed", __FUNCTION__, __LINE__);
        deinit();
        return -1;
    }
    
    // load yuv
    const char *yuv_path = "/sdcard/test_720x576.yuv";
    LOGI("natvie_SDL %s", yuv_path);
    yuv_frame.width = 720;
    yuv_frame.height = 576;    
    if (!load_yuv(yuv_path))
    {
        LOGE("%s %d load yuv failed", __FUNCTION__, __LINE__);
        deinit();
        return -2;
    }
    
    // texture operation
    texture_create(yuv_frame.width, yuv_frame.height);

    texture_update();
    texture_render();
    
    
    SDL_Delay(2000);
    
    // free resources
    unload_yuv();
    texture_destory();
    deinit();

    // Quit SDL
    SDL_Quit();
	
    return 0;
}
